/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
 */
package td.domain;

import java.util.ArrayList;
import java.util.Date;

import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Ignore;

/**
 * All statistical entries that form hierarchies.
 * 
 * @author Artem Shamsutdinov
 * 
 * @param <H>
 */
@SuppressWarnings("serial")
@Entity
public abstract class Hierarchy<H extends Hierarchy<H>> extends StatisticalGrouping {
	protected Integer higherarchyLevel;
	protected Long parentId;
	
	// Statistics
	private Long numTopics = 0L;
	private Date lastTopicAdded;
	private Date directLastThoughtAdded;
	private Date directLastThoughtRead;
	private Long directNumThoughts = 0L;
	private Long directNumReferences = 0L;
	private Long directNumTaggings = 0L;
	private Long directNumThoughtReads = 0L;
	private Date directLastTagged;
	private Long directNumTopics = 0L;
	private Date directLastTopicAdded;

	@Ignore
	private ArrayList<H> children;
	@Ignore
	protected H parent;
	
	@Ignore
	private ArrayList<Topic> topics = new ArrayList<Topic>();

	public Hierarchy() {
	}

	public Hierarchy(String name) {
		this(name, null, null);
	}

	public Hierarchy(String name, String description) {
		this(name, description, null);
	}

	public Hierarchy(String name, String description, H parent) {
		super();
		this.name = name;
		this.description = description;
		this.parent = parent;
		if (parent != null) {
			this.parentId = parent.getId();
			this.higherarchyLevel = parent.higherarchyLevel + 1;
		} else {
			this.higherarchyLevel = 0;
		}
	}

	public H addChild(String name) {
		return addChild(name, null);
	}

	public H addChild(String name, String description) {
		if (children == null) {
			children = new ArrayList<H>();
		}
		@SuppressWarnings("unchecked")
		H child = getInstance(name, description, (H) this);
		children.add(child);
		return child;
	}

	protected abstract H getInstance(String name, String description, H parent);

	/**
	 * @return the topics
	 */
	public ArrayList<Topic> getTopics() {
		return topics;
	}
	/**
	 * @param topics the topics to set
	 */
	public void setTopics(ArrayList<Topic> topics) {
		this.topics = topics;
	}
	
	public Integer getHigherarchyLevel() {
		return higherarchyLevel;
	}

	public void setHigherarchyLevel(Integer higherarchyLevel) {
		this.higherarchyLevel = higherarchyLevel;
	}

	public Long getParentId() {
		return parentId;
	}

	public void setParentId(Long parentId) {
		this.parentId = parentId;
	}

	public H getParent() {
		return parent;
	}

	public void setParent(H parent) {
		this.parent = parent;
	}

	public ArrayList<H> getChildren() {
		return children;
	}

	public void setChildren(ArrayList<H> children) {
		this.children = children;
	}

	public void setThoughts(ArrayList<Thought> thoughts) {
		this.thoughts = thoughts;
	}

	public Long getNumTopics() {
		return numTopics;
	}

	public void setNumTopics(Long numTopics) {
		this.numTopics = numTopics;
	}

	public Date getLastTopicAdded() {
		return lastTopicAdded;
	}

	public void setLastTopicAdded(Date lastTopicAdded) {
		this.lastTopicAdded = lastTopicAdded;
	}

	public Date getDirectLastThoughtAdded() {
		return directLastThoughtAdded;
	}

	public void setDirectLastThoughtAdded(Date directLastThoughtAdded) {
		this.directLastThoughtAdded = directLastThoughtAdded;
	}

	public Date getDirectLastThoughtRead() {
		return directLastThoughtRead;
	}

	public void setDirectLastThoughtRead(Date directLastThoughtRead) {
		this.directLastThoughtRead = directLastThoughtRead;
	}

	public Long getDirectNumThoughts() {
		return directNumThoughts;
	}

	public void setDirectNumThoughts(Long directNumThoughts) {
		this.directNumThoughts = directNumThoughts;
	}

	public Long getDirectNumReferences() {
		return directNumReferences;
	}

	public void setDirectNumReferences(Long directNumReferences) {
		this.directNumReferences = directNumReferences;
	}

	public Long getDirectNumTaggings() {
		return directNumTaggings;
	}

	public void setDirectNumTaggings(Long directNumTaggings) {
		this.directNumTaggings = directNumTaggings;
	}

	public Long getDirectNumThoughtReads() {
		return directNumThoughtReads;
	}

	public void setDirectNumThoughtReads(Long directNumThoughtReads) {
		this.directNumThoughtReads = directNumThoughtReads;
	}

	public Date getDirectLastTagged() {
		return directLastTagged;
	}

	public void setDirectLastTagged(Date directLastTagged) {
		this.directLastTagged = directLastTagged;
	}

	public Long getDirectNumTopics() {
		return directNumTopics;
	}

	public void setDirectNumTopics(Long directNumTopics) {
		this.directNumTopics = directNumTopics;
	}

	public Date getDirectLastTopicAdded() {
		return directLastTopicAdded;
	}

	public void setDirectLastTopicAdded(Date directLastTopicAdded) {
		this.directLastTopicAdded = directLastTopicAdded;
	}

	@Override
	public int compareTo(Statistical otherStats) {
		if (otherStats instanceof Hierarchy) {
			Hierarchy<?> otherHierarchy = (Hierarchy<?>) otherStats;
			switch (getComparisonType()) {
			case NUMBER_OF_TOPICS:
				return otherHierarchy.numTopics.compareTo(numTopics);
			case LAST_TOPIC_ADDED_DATE:
				return otherHierarchy.lastTopicAdded.compareTo(lastTopicAdded);
			case DIRECT_LAST_THOUGHT_ADDED_DATE:
				return otherHierarchy.directLastThoughtAdded
						.compareTo(directLastThoughtAdded);
			case DIRECT_LAST_THOUGHT_READ_DATE:
				return otherHierarchy.directLastThoughtRead
						.compareTo(directLastThoughtRead);
			case DIRECT_NUMBER_OF_THOUGHT_READS:
				return otherHierarchy.directNumThoughtReads
						.compareTo(directNumThoughtReads);
			case DIRECT_NUMBER_OF_THOUGHTS:
				return otherHierarchy.directNumThoughts
						.compareTo(directNumThoughts);
			case DIRECT_NUMBER_OF_REFERENCES:
				return otherHierarchy.directNumReferences
						.compareTo(directNumReferences);
			case DIRECT_NUMBER_OF_TAGGINGS:
				return otherHierarchy.directNumTaggings
						.compareTo(directNumTaggings);
			case DIRECT_LAST_TAGGED_DATE:
				return otherHierarchy.directLastTagged
						.compareTo(directLastTagged);
			case DIRECT_NUMBER_OF_TOPICS:
				return otherHierarchy.directNumTopics
						.compareTo(directNumTopics);
			case DIRECT_LAST_TOPIC_ADDED_DATE:
				return otherHierarchy.directLastTopicAdded
						.compareTo(directLastTopicAdded);
			default:
				return super.compareTo(otherHierarchy);
			}
		}
		throw new RuntimeException(
				"Hierarchies should only be compared to other Hierarchies");
	}

}